package com.aipanda.entity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.Valid;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;

import com.aipanda.pojo.Setting;
import com.aipanda.util.SettingUtils;

/**
 * 订单类
 * @param sn					订单编号
 * @param paymentStatus			支付状态
 * @param shippingStatus		配送状态
 * @param fee					支付手续费
 * @param freight				运费
 * @param discount				优惠金额[优惠劵金额数]
 * @param amountPaid			已付金额
 * @param point					赠送积分
 * @param consignee				收货人
 * @param areaName				区域名称
 * @param address				地址
 * @param zipCode				邮编
 * @param phone					电话
 * @param isInvoice				是否开据发票
 * @param tax					发票税金
 * @param memo					附言
 * @param promotion				设置促销类型名称
 * @param expire				订单过期时间【如果选择的支付方式有时间控制的话】
 * @param lockExpire			订单锁定时间【锁定订单多少秒，管理人员不能操作，过了锁定时间，用户不能再进行操作】
 * @param isAllocatedStock		是否需要分配库存【如果发货了，就不需要分配库存了】
 * @param paymentMethodName		支付方式名称
 * @param shippingMethodName	配送方式名称
 * @param area					地区
 * @param paymentMethod			支付方式
 * @param shippingMethod		配送方式
 * @param operator				操作员
 * @param member				会员
 * @param couponCode			优惠券码
 * @param coupons				优惠券类型
 * @param orderItems			订单明细表
 * @param orderLogs				订单日志
 * @param deposits				预存款
 * @param payments
 * @param refunds
 * @param shippings
 * @param returns
 * @param orderStatus 			订单状态
 * 
 * @author wanyuxiang
 *
 */
@Entity
@Table(name = "aipanda_order")
public class Order extends BaseEntity {
	private static final long serialVersionUID = 8370942500343156156L;
	private String sn;
	private PaymentStatus paymentStatus;
	private ShippingStatus shippingStatus;
	private BigDecimal fee;
	private BigDecimal freight;
	private BigDecimal discount;
	private BigDecimal amountPaid;
	private Integer point;
	private String consignee;
	private String areaName;
	private String address;
	private String zipCode;
	private String phone;
	private Boolean isInvoice;
	private String invoiceTitle;
	private BigDecimal tax;
	private String memo;
	private String promotion;
	private Date expire;
	private Date lockExpire;
	private Boolean isAllocatedStock;
	private String paymentMethodName;
	private String shippingMethodName;
	private Area area;
	private PaymentMethod paymentMethod;
	private ShippingMethod shippingMethod;
	private Admin operator;
	private Member member;
	private CouponCode couponCode;
	private List<Coupon> coupons = new ArrayList<Coupon>();
	private List<OrderItem> orderItems = new ArrayList<OrderItem>();
	private Set<OrderLog> orderLogs = new HashSet<OrderLog>();
	private Set<Deposit> deposits = new HashSet<Deposit>();
	private Set<Payment> payments = new HashSet<Payment>();
	private Set<Refunds> refunds = new HashSet<Refunds>();
	private Set<Shipping> shippings = new HashSet<Shipping>();
	private Set<Returns> returns = new HashSet<Returns>();
	private OrderStatus orderStatus;
	
	/**
	 * description 订单状态
	 * @enum 	unconfirmed  	未确认<br/>
	 * 			confirmed		确认<br/>
	 * 			completed		已完成 <br/>
	 * 			cancelled		已取消<br/>
	 * @author wanyuxiang
	 *
	 */
	public enum OrderStatus {
		unconfirmed, confirmed, completed, cancelled;
	}
	
	/**
	 * description 支付状态
	 * @enum 	unpaid  			未支付<br/>
	 * 			partialPayment		部份支付<br/>
	 * 			paid				付清的<br/>
	 * 			partialRefunds		部份退款<br/>
	 * 			refunded			退款<br/>
	 * @author wanyuxiang
	 *
	 */
	public enum PaymentStatus{
	  unpaid, partialPayment, paid, partialRefunds, refunded;
	}
	
	/**
	 * description 配送状态
	 * @enum 	unshipped  			未发货<br/>
	 * 			partialShipment		部份退货<br/>
	 * 			shipped				已发货<br/>
	 * 			partialReturns		部份退款<br/>
	 * 			returned			已退货 <br/>
	 * @author wanyuxiang
	 *
	 */
	public enum ShippingStatus{
	  unshipped, partialShipment, shipped, partialReturns, returned;
	}
	
	

	@Column(nullable = false, updatable = false, unique = true)
	public String getSn() {
		return this.sn;
	}

	public void setSn(String sn) {
		this.sn = sn;
	}

	@Column(name="order_status",nullable = false)
	public OrderStatus getOrderStatus() {
		return this.orderStatus;
	}

	public void setOrderStatus(OrderStatus orderStatus) {
		this.orderStatus = orderStatus;
	}

	@Column(name="payment_status",nullable = false)
	public PaymentStatus getPaymentStatus() {
		return this.paymentStatus;
	}

	public void setPaymentStatus(PaymentStatus paymentStatus) {
		this.paymentStatus = paymentStatus;
	}

	@Column(name="shipping_status",nullable = false)
	public ShippingStatus getShippingStatus() {
		return this.shippingStatus;
	}

	public void setShippingStatus(ShippingStatus shippingStatus) {
		this.shippingStatus = shippingStatus;
	}

	@Column(nullable = false, precision = 21, scale = 6)
	public BigDecimal getFee() {
		return this.fee;
	}

	public void setFee(BigDecimal fee) {
		this.fee = fee;
	}

	@NotNull
	@Min(0L)
	@Digits(integer = 15, fraction = 6)
	@Column(nullable = false, precision = 21, scale = 6)
	public BigDecimal getFreight() {
		return this.freight;
	}

	public void setFreight(BigDecimal freight) {
		this.freight = freight;
	}

	@NotNull
	@Min(0L)
	@Digits(integer = 15, fraction = 6)
	@Column(nullable = false, precision = 21, scale = 6)
	public BigDecimal getDiscount() {
		return this.discount;
	}

	public void setDiscount(BigDecimal discount) {
		this.discount = discount;
	}

	@Column(name="amount_paid",nullable = false, precision = 21, scale = 6)
	public BigDecimal getAmountPaid() {
		return this.amountPaid;
	}

	public void setAmountPaid(BigDecimal amountPaid) {
		this.amountPaid = amountPaid;
	}

	@NotNull
	@Min(0L)
	@Column(nullable = false)
	public Integer getPoint() {
		return this.point;
	}

	public void setPoint(Integer point) {
		this.point = point;
	}

	@NotEmpty
	@Length(max = 255)
	@Column(nullable = false)
	public String getConsignee() {
		return this.consignee;
	}

	public void setConsignee(String consignee) {
		this.consignee = consignee;
	}

	@Column(name="area_name",nullable = false)
	public String getAreaName() {
		return this.areaName;
	}

	public void setAreaName(String areaName) {
		this.areaName = areaName;
	}

	@NotEmpty
	@Length(max = 255)
	@Column(nullable = false)
	public String getAddress() {
		return this.address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@NotEmpty
	@Length(max = 255)
	@Column(name="zip_code",nullable = false)
	public String getZipCode() {
		return this.zipCode;
	}

	public void setZipCode(String zipCode) {
		this.zipCode = zipCode;
	}

	@NotEmpty
	@Length(max = 255)
	@Column(nullable = false)
	public String getPhone() {
		return this.phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}

	@NotNull
	@Column(name="is_invoice",nullable = false)
	public Boolean getIsInvoice() {
		return this.isInvoice;
	}

	public void setIsInvoice(Boolean isInvoice) {
		this.isInvoice = isInvoice;
	}

	@Length(max = 255)
	@Column(name="invoice_title")
	public String getInvoiceTitle() {
		return this.invoiceTitle;
	}

	public void setInvoiceTitle(String invoiceTitle) {
		this.invoiceTitle = invoiceTitle;
	}

	@Min(0L)
	@Digits(integer = 15, fraction = 6)
	@Column(nullable = false, precision = 21, scale = 6)
	public BigDecimal getTax() {
		return this.tax;
	}

	public void setTax(BigDecimal tax) {
		this.tax = tax;
	}

	@Length(max = 255)
	public String getMemo() {
		return this.memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	@Column(updatable = false)
	public String getPromotion() {
		return this.promotion;
	}

	public void setPromotion(String promotion) {
		this.promotion = promotion;
	}

	public Date getExpire() {
		return this.expire;
	}

	public void setExpire(Date expire) {
		this.expire = expire;
	}
	@Column(name="lock_expire")
	public Date getLockExpire() {
		return this.lockExpire;
	}

	public void setLockExpire(Date lockExpire) {
		this.lockExpire = lockExpire;
	}

	@Column(name="is_allocated_stock",nullable = false)
	public Boolean getIsAllocatedStock() {
		return this.isAllocatedStock;
	}

	public void setIsAllocatedStock(Boolean isAllocatedStock) {
		this.isAllocatedStock = isAllocatedStock;
	}

	@Column(name="payment_method_name",nullable = false)
	public String getPaymentMethodName() {
		return this.paymentMethodName;
	}

	public void setPaymentMethodName(String paymentMethodName) {
		this.paymentMethodName = paymentMethodName;
	}

	@Column(name="shipping_method_name",nullable = false)
	public String getShippingMethodName() {
		return this.shippingMethodName;
	}

	public void setShippingMethodName(String shippingMethodName) {
		this.shippingMethodName = shippingMethodName;
	}

	@NotNull
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="area",nullable = false)
	public Area getArea() {
		return this.area;
	}

	public void setArea(Area area) {
		this.area = area;
	}

	@NotNull
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="payment_method",nullable = false)
	public PaymentMethod getPaymentMethod() {
		return this.paymentMethod;
	}

	public void setPaymentMethod(PaymentMethod paymentMethod) {
		this.paymentMethod = paymentMethod;
	}

	@NotNull
	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="shipping_method")
	public ShippingMethod getShippingMethod() {
		return this.shippingMethod;
	}

	public void setShippingMethod(ShippingMethod shippingMethod) {
		this.shippingMethod = shippingMethod;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="operator")
	public Admin getOperator() {
		return this.operator;
	}

	public void setOperator(Admin operator) {
		this.operator = operator;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="member",nullable = false, updatable = false)
	public Member getMember() {
		return this.member;
	}

	public void setMember(Member member) {
		this.member = member;
	}

	@OneToOne(fetch = FetchType.LAZY)
	@JoinColumn(name="coupon_code")
	public CouponCode getCouponCode() {
		return this.couponCode;
	}

	public void setCouponCode(CouponCode couponCode) {
		this.couponCode = couponCode;
	}

	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(name = "aipanda_order_coupon",joinColumns ={@JoinColumn(name="orders")},inverseJoinColumns = {@JoinColumn(name="coupons")})
	public List<Coupon> getCoupons() {
		return this.coupons;
	}

	public void setCoupons(List<Coupon> coupons) {
		this.coupons = coupons;
	}

	@Valid
	@NotEmpty
	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = {CascadeType.ALL }, orphanRemoval=true)
	@OrderBy("isGift asc")
	public List<OrderItem> getOrderItems() {
		return this.orderItems;
	}

	public void setOrderItems(List<OrderItem> orderItems) {
		this.orderItems = orderItems;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
	@OrderBy("createDate asc")
	public Set<OrderLog> getOrderLogs() {
		return this.orderLogs;
	}

	public void setOrderLogs(Set<OrderLog> orderLogs) {
		this.orderLogs = orderLogs;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
	public Set<Deposit> getDeposits() {
		return this.deposits;
	}

	public void setDeposits(Set<Deposit> deposits) {
		this.deposits = deposits;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
	@OrderBy("createDate asc")
	public Set<Payment> getPayments() {
		return this.payments;
	}

	public void setPayments(Set<Payment> payments) {
		this.payments = payments;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
	@OrderBy("createDate asc")
	public Set<Refunds> getRefunds() {
		return this.refunds;
	}

	public void setRefunds(Set<Refunds> refunds) {
		this.refunds = refunds;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
	@OrderBy("createDate asc")
	public Set<Shipping> getShippings() {
		return this.shippings;
	}

	public void setShippings(Set<Shipping> shippings) {
		this.shippings = shippings;
	}

	@OneToMany(mappedBy = "order", fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
	@OrderBy("createDate asc")
	public Set<Returns> getReturns() {
		return this.returns;
	}

	public void setReturns(Set<Returns> returns) {
		this.returns = returns;
	}

	@Transient
	public String getProductName() {
		StringBuffer sb = new StringBuffer();
		if (getOrderItems() != null) {
			Iterator<OrderItem> iterator = getOrderItems().iterator();
			while (iterator.hasNext()) {
				OrderItem orderItem = iterator.next();
				if (orderItem == null|| orderItem.getFullName() == null){
					continue;
				}
				sb.append(" ").append(orderItem.getFullName());
			}
			if (sb.length() > 0){
				sb.deleteCharAt(0);
			}
		}
		return sb.toString();
	}

	@Transient
	public int getWeight() {
		int i = 0;
		if (getOrderItems() != null) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if (localOrderItem == null)
					continue;
				i += localOrderItem.getTotalWeight();
			}
		}
		return i;
	}

	@Transient
	public int getQuantity() {
		int i = 0;
		if (getOrderItems() != null) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if ((localOrderItem == null)
						|| (localOrderItem.getQuantity() == null))
					continue;
				i += localOrderItem.getQuantity().intValue();
			}
		}
		return i;
	}

	@Transient
	public int getShippedQuantity() {
		int i = 0;
		if (getOrderItems() != null) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if ((localOrderItem == null)
						|| (localOrderItem.getShippedQuantity() == null))
					continue;
				i += localOrderItem.getShippedQuantity().intValue();
			}
		}
		return i;
	}

	@Transient
	public int getReturnQuantity() {
		int i = 0;
		if (getOrderItems() != null) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if ((localOrderItem == null)
						|| (localOrderItem.getReturnQuantity() == null))
					continue;
				i += localOrderItem.getReturnQuantity().intValue();
			}
		}
		return i;
	}

	@Transient
	public BigDecimal getPrice() {
		BigDecimal localBigDecimal = new BigDecimal(0);
		if (getOrderItems() != null) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if ((localOrderItem == null)
						|| (localOrderItem.getSubtotal() == null))
					continue;
				localBigDecimal = localBigDecimal.add(localOrderItem
						.getSubtotal());
			}
		}
		return localBigDecimal;
	}

	//获得产品总金额
	@Transient
	public BigDecimal getTotalAmount() {
		
		BigDecimal discount = getDiscount() != null ? getDiscount() : new BigDecimal(0);
		
		//运费
		BigDecimal freight = getFreight() != null ? getFreight() : new BigDecimal(0);
		
		BigDecimal fee = getFee() != null ? getFee() : new BigDecimal(0);
		//税金
		BigDecimal tax = getTax() != null ? getTax() : new BigDecimal(0);
		
		BigDecimal totalAmount = getPrice().subtract(discount).add(freight).add(fee).add(tax);
		
		
		
		return totalAmount.compareTo(new BigDecimal(0)) > 0 ? totalAmount: new BigDecimal(0);
	}

	/**
	 * 应付金额
	 * @return
	 */
	@Transient
	public BigDecimal getAmountPayable() {
		//getAmount()getAmountPaid()/应付金额=获得产品总金额-获得已付金额
		BigDecimal amountPayable = getTotalAmount().subtract(getAmountPaid());
		return amountPayable.compareTo(new BigDecimal(0)) > 0 ? amountPayable: new BigDecimal(0);
	}

	/**
	 * 判断订单是否过期
	 * @return
	 */
	@Transient
	public boolean isExpired() {
		//订单过期时间不为NULL&&当前时间是否在过期时间之后
		return (getExpire() != null) && (new Date().after(getExpire()));
	}

	@Transient
	public OrderItem getOrderItem(String sn) {
		if ((sn != null) && (getOrderItems() != null)) {
			Iterator<OrderItem> localIterator = getOrderItems().iterator();
			while (localIterator.hasNext()) {
				OrderItem localOrderItem = localIterator.next();
				if ((localOrderItem != null)
						&& (sn.equalsIgnoreCase(localOrderItem.getSn())))
					return localOrderItem;
			}
		}
		return null;
	}

	/**
	 * 判断订单是否被锁定
	 * @param operator  操作人员
	 * @return  true[锁定]/false[未锁定]
	 */
	@Transient
	public boolean isLocked(Admin operator) {
		//判断订单锁定时间是否为空&&当前时间是否在锁定时间之前&&订单操作员是否是当前操作员
		return (getLockExpire() != null)&& (new Date().before(getLockExpire()))&& (getOperator() != operator);
	}

	@Transient
	public BigDecimal calculateTax() {
		Setting localSetting = SettingUtils.get();
		BigDecimal localBigDecimal;
		if (localSetting.getIsTaxPriceEnabled().booleanValue())
			localBigDecimal = getPrice().subtract(getDiscount()).multiply(
					new BigDecimal(localSetting.getTaxRate().toString()));
		else
			localBigDecimal = new BigDecimal(0);
		return localSetting.setPriceScale(localBigDecimal);
	}
	
	/**
	 * Save到数据库之前
	 * @param order
	 * @return
	 */
	public Order prePersist(Order order) {
		if (order.getArea() != null){
			order.setAreaName(order.getArea().getFullName());
		}
		if (order.getPaymentMethod() != null){
			order.setPaymentMethodName(order.getPaymentMethod().getName());
		}
		if (order.getShippingMethod() != null){
			order.setShippingMethodName(order.getShippingMethod().getName());
		}
		return order;
	}
	/**
	 * Update到数据库之前
	 * @param order
	 * @return
	 */
	public Order preUpdate(Order order) {
		if (order.getArea() != null){
			order.setAreaName(order.getArea().getFullName());
		}
		if (order.getPaymentMethod() != null){
			order.setPaymentMethodName(order.getPaymentMethod().getName());
		}
		if (order.getShippingMethod() != null){
			order.setShippingMethodName(order.getShippingMethod().getName());
		}
		return order;
	}
	/**
	 * Delete到数据库之前
	 * @param order
	 * @return
	 */
	public Order preRemove(Order order) {
		Set<Deposit> set = order.getDeposits();
		if (set != null) {
			Iterator<Deposit> iterator = set.iterator();
			while (iterator.hasNext()) {
				Deposit deposit = iterator.next();
				deposit.setOrder(null);
			}
		}
		return order;
	}
}
